Nginx + Certbot + продление в cron - это три движущиеся части там, где хватило бы одной. Caddy сам выпускает и продлевает TLS-сертификаты, а его конфиг - несколько строк. Ставим на Ubuntu, пишем Caddyfile для статики и для прокси на приложение, разбираем, как работает ACME.
Настройка веб-сервера Caddy - это один apt-репозиторий, три-четыре строки в конфиге и ноль возни с сертификатами. Caddy сам получает TLS-сертификат, как только домен появляется в активной конфигурации, заранее его продлевает и умеет работать обратным прокси. Если вы держите небольшой сайт или приложение на VPS и устали от связки Nginx + Certbot + задача в cron на продление, ниже - как поставить Caddy на Ubuntu 22.04, 24.04 и 26.04 и написать первый конфиг: для статического сайта и для прокси на приложение.
TL;DR. Подключите официальный apt-репозиторий Caddy (он на Cloudsmith), поставьте пакет
caddy- вместе с ним придёт systemd-сервис. Опишите сайт в/etc/caddy/Caddyfile: для статики -rootиfile_server, для приложения -reverse_proxy localhost:3000. Проверьте конфиг командойcaddy validate, примените черезsystemctl reload caddy. Домен должен A- и/или AAAA-записью указывать на сервер, а из портов 80 и 443 снаружи должен быть доступен хотя бы один: тогда Caddy сам получит публично доверенный сертификат, обычно через Let's Encrypt, с ZeroSSL как запасным вариантом. Сертификаты и ключи лежат в/var/lib/caddy/.local/share/caddy, логи - вjournalctl -u caddy.
Caddy - веб-сервер на Go, из той же категории, что Nginx или Apache: отдаёт статические файлы, проксирует запросы на приложения, терминирует HTTPS. Разница в одном: здесь HTTPS включён по умолчанию. Certbot - это утилита Let's Encrypt для выпуска сертификатов; с Nginx её ставят отдельно, настраивают плагин или webroot и добавляют системный таймер на продление. Caddy делает всё это внутри себя. Как только домен попадает в активную конфигурацию, Caddy в фоне обращается к центру сертификации, проходит проверку владения, кладёт сертификат в память и на диск, дальше продлевает заранее - примерно за треть срока до истечения. Ждать первого запроса пользователя для этого не нужно, отдельного демона, задачи в cron и перезапуска - тоже.
На конфиге разница такая же заметная. Server-блок Nginx под HTTPS-прокси - это 15-20 строк с listen 443 ssl, ssl_certificate, набором proxy_set_header. В Caddyfile то же самое - две строки. Caddy не вытесняет Nginx во всех сценариях (об этом в конце), но для типовой задачи «один-три сайта на VPS, нужен HTTPS и прокси на бэкенд» он убирает почти всю рутину.
Caddy будет слушать TCP-порты 80 и 443, но сам firewall не трогает. Если эти порты разрешены на входящие, веб-сервер станет доступен из интернета, то есть машина становится видимой снаружи. Если VPS только что создан, сначала закройте лишние порты и настройте вход по ключу - это отдельная тема, см. «Защита свежего VPS». Дальше считаем, что root или sudo-пользователь у вас есть, а firewall пропускает входящие на 80 и 443 (TCP, а для HTTP/3 ещё UDP 443).
Ставим из официального репозитория проекта - он размещён на Cloudsmith. Для Ubuntu 24.04 и 26.04 пакет caddy есть и в репозитории самой Ubuntu, но там заметно более старая ветка; в Ubuntu 22.04 пакета Caddy в стандартном репозитории нет вовсе. Репозиторий проекта даёт актуальный стабильный релиз и обновления обычным apt update. Первый блок ставит зависимости, кладёт публичный ключ репозитория и записывает его адрес.
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
debian-keyring, debian-archive-keyring - наборы ключей, которыми apt проверяет подписи репозиториев.gpg --dearmor -o ...caddy-stable-archive-keyring.gpg - кладёт ключ Caddy туда, где apt его ждёт.curl пишет файл со строкой репозитория в /etc/apt/sources.list.d/; tee здесь нужен, потому что запись идёт от root.chmod o+r открывают ключ и файл-список на чтение служебному пользователю apt (_apt). На «зажатых» образах с жёстким umask без них apt update падает с Permission denied.Теперь обновляем индекс пакетов и ставим Caddy.
sudo apt update
sudo apt install caddy
Что вы должны увидеть. В выводе apt update - строка про репозиторий Caddy (...cloudsmith.io/public/caddy/stable...) без ошибок GPG. Если apt ругается на просроченный ключ (EXPKEYSIG), удалите старый ключ и запишите заново: sudo rm /usr/share/keyrings/caddy-stable-archive-keyring.gpg, затем повторите строку с curl ... gpg.key - ключ репозитория периодически перевыпускают. После apt install caddy пакет создаёт системного пользователя caddy, кладёт бинарник в /usr/bin/caddy, конфиг-заготовку в /etc/caddy/Caddyfile и запускает systemd-сервис caddy. systemd - штатная система запуска сервисов в Linux: поднимает процесс при загрузке машины и перезапускает его, если тот упал.
Проверяем, что всё встало.
caddy version
systemctl status caddy
Что вы должны увидеть. caddy version печатает строку вида v2.11.4 h1:.... systemctl status caddy - зелёное active (running). Если сразу открыть в браузере http://IP-сервера, Caddy отдаст страницу-заглушку со ссылкой на документацию - значит, сервис живой. Эта заготовка обслуживает только заглушку на порту 80; дальше вы её замените своим конфигом.
Когда в Caddyfile адрес сайта - это доменное имя (не IP и не localhost), Caddy сам включает HTTPS. Механизм - ACME (Automatic Certificate Management Environment), стандартный протокол выпуска сертификатов; по нему же работает Certbot. По умолчанию Caddy пробует два центра сертификации подряд: сначала Let's Encrypt, затем ZeroSSL. Управление сертификатами Caddy запускает сразу после загрузки конфигурации и ведёт в фоне - первого запроса пользователя не ждёт.
Чтобы выдать сертификат, центр сертификации должен убедиться, что домен ваш. Caddy отвечает на один из проверочных запросов (challenge): HTTP-01 на порту 80 или TLS-ALPN-01 на порту 443 - достаточно, чтобы снаружи был доступен любой из этих двух портов, а домен A- и/или AAAA-записью указывал на этот сервер. Если оба порта закрыты firewall или заняты Nginx, проверка не пройдёт, и в логах будет ошибка ACME. Обойти требование входящих 80/443 можно проверкой DNS-01 (нужен плагин под вашего DNS-провайдера и пересборка через xcaddy) - тогда открытые порты для выпуска не нужны вовсе.
Для localhost и внутренних имён публично доверенный сертификат обычно не используется. Для них, а по умолчанию и для IP-адресов и всего, где вы явно написали tls internal, Caddy поднимает собственный локальный центр сертификации и подписывает сертификат сам (публично доверенный сертификат на IP в принципе возможен у некоторых центров, но это не путь по умолчанию). Браузер такому сертификату не доверяет, пока вы не добавите корневой сертификат Caddy в доверенные хранилища - он лежит в /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt. Для внутренних сервисов и локальной разработки этого достаточно.
Сервис caddy работает от пользователя caddy с домашним каталогом /var/lib/caddy. Сертификаты, приватные ключи и данные ACME хранятся в /var/lib/caddy/.local/share/caddy (внутри - подкаталог certificates). Автосохранённый JSON-конфиг - в /var/lib/caddy/.config/caddy. Пока сервис работает штатно, эти файлы трогать не нужно: продление идёт само.
Caddyfile - основной конфиг Caddy, по умолчанию /etc/caddy/Caddyfile. Синтаксис короткий: строка с адресом сайта, дальше в фигурных скобках - директивы. Заменим заготовку на раздачу статики из каталога. Заранее положите файлы сайта, например в /var/www/example.com, и проверьте, что example.com A- и/или AAAA-записью указывает на сервер (если остался старый AAAA на другую машину - уберите его, иначе ACME будет вести себя странно). Убедитесь, что пользователь caddy может читать эти файлы и заходить в каталоги по пути (право на чтение и x на директориях) - иначе вместо страницы будет 403. Приведите /etc/caddy/Caddyfile к такому виду:
example.com {
root * /var/www/example.com
file_server
encode zstd gzip
}
example.com - адрес сайта. Домен без http:// - сигнал Caddy включить HTTPS.root * /var/www/example.com - корневой каталог сайта; * означает «для всех путей запроса».file_server - отдавать файлы с диска.encode zstd gzip - сжимать ответы (zstd, с откатом на gzip). Необязательно, но почти всегда полезно.Перед применением проверьте конфиг. caddy fmt приводит отступы к единому виду, caddy validate читает конфиг и говорит, корректен ли синтаксис и где ошибка. Делайте это перед каждым reload.
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
Что вы должны увидеть. caddy validate при успехе печатает Valid configuration. Если есть ошибка, он назовёт строку и причину - правьте и запускайте снова. Теперь применяем без остановки сервиса:
sudo systemctl reload caddy
Команда завершается молча, без вывода - это нормально. Через несколько секунд проверьте результат:
curl -I https://example.com
journalctl -u caddy -n 30 --no-pager
Что вы должны увидеть. В ответе curl - успешный HTTP-ответ без ошибки TLS; конкретный код (200, 301, ...) и версия протокола зависят от сайта и сборки curl. В логах - запись про certificate obtained successfully для example.com. Если сертификат ещё выпускается, повторите curl через полминуты. Ошибка вида SSL certificate problem в первые секунды после reload - обычно просто гонка: подождите и проверьте ещё раз.
Обратный прокси (reverse proxy) - сервер, который принимает запросы снаружи и передаёт их приложению внутри машины, а ответ возвращает клиенту. Адрес, куда прокси шлёт запросы, называют upstream. Схема нужна, когда ваш сервис на Node.js, Python или Go слушает localhost:3000, а наружу его надо отдавать по HTTPS на 443. В Caddyfile это выглядит так:
app.example.com {
reverse_proxy localhost:3000
}
app.example.com - поддомен, A-запись на тот же сервер.reverse_proxy localhost:3000 - слать запросы на приложение по этому адресу. Для upstream по HTTP Caddy передаёт исходный Host клиента без изменений и добавляет X-Forwarded-For, X-Forwarded-Proto и X-Forwarded-Host - приложение видит реальный адрес, протокол и хост клиента. (Для сравнения: proxy_pass в Nginx по умолчанию переписывает Host на адрес upstream - Caddy так не делает.)Несколько сайтов в одном файле - это просто несколько блоков подряд, статических и прокси вперемешку. После правки снова прогоните caddy validate и systemctl reload caddy, затем проверьте:
curl -I https://app.example.com
Что вы должны увидеть. Успешный ответ или тот код, что отдаёт само приложение (200, 302, ...). Если пришло 502 Bad Gateway - прокси работает, но upstream недоступен: проверьте, слушает ли сервис порт 3000.
sudo ss -tlnp | grep 3000
Пустой вывод означает, что на 3000 никто не слушает - запустите приложение или поправьте порт в конфиге.
reload применяет новый конфиг без остановки сервиса и без разрыва соединений: Caddy поднимает новую конфигурацию рядом и переключается на неё. Для правок Caddyfile всегда используйте его. restart останавливает и заново запускает процесс, то есть даёт короткий простой; он нужен, только если сервис завис или вы меняли сам unit-файл (тогда сначала sudo systemctl daemon-reload). Под капотом systemctl reload caddy вызывает caddy reload --config /etc/caddy/Caddyfile --force.
Что | Где |
|---|---|
Основной конфиг | |
Бинарник | |
Сертификаты, ключи, данные ACME | |
Автосохранённый JSON-конфиг | |
Unit systemd | |
Логи | |
journalctl -u caddy - это служебные логи Caddy: старт, выпуск и продление сертификатов, ошибки. Логи запросов (access logs) по умолчанию выключены - их включают директивой log в Caddyfile.
Аспект | Caddy | Nginx |
|---|---|---|
TLS-сертификаты | выпускает и продлевает сам | отдельно Certbot + системный таймер |
Конфиг HTTPS-прокси | 2-3 строки | 15-20 строк |
Формат конфига | Caddyfile или JSON через API | собственный синтаксис |
Применение изменений | reload без простоя | reload без простоя |
HTTP/3 (QUIC) | из коробки | с 1.25, отдельной директивой |
Расширения | плагины, пересборка через xcaddy | модули, часто пересборка; готовых больше |
Статика под высокой нагрузкой | хорошо | традиционно чуть быстрее, тоньше тюнится |
Экосистема и примеры | меньше | огромная, почти любой кейс уже описан |
Оставайтесь на Nginx, если: у вас уже есть большой рабочий конфиг и нет причин его переписывать; нужен тонкий контроль над TLS - конкретные шифры и версии, нестандартные сценарии с клиентскими сертификатами; логика маршрутизации и rewrite сложная и в Nginx выражается проще; нужны его модули (RTMP, широкий Lua/OpenResty); важны формальная поддержка и то, что Nginx лежит в основном репозитории Ubuntu. В остальных случаях Caddy экономит время: HTTPS и продление перестают быть задачей, конфиг читается целиком за минуту, обратный прокси - одна строка.
Сам сервис работает от системного пользователя caddy, но sudo нужен при установке и для правок конфига в /etc/caddy. Право слушать порты 80 и 443 выдано процессу через capability в unit-файле, отдельных действий не требует. Для локального центра сертификации Caddy пытается добавить свой корневой сертификат в системное хранилище - под сервисным пользователем это может не сработать, тогда добавьте его вручную из data-каталога.
Да. В блоке сайта укажите tls /path/fullchain.pem /path/privkey.pem, и Caddy возьмёт ваши файлы вместо ACME. Так делают, когда сертификат выдан корпоративным центром или куплен у коммерческого поставщика.
Проверка HTTP-01 не пройдёт. Caddy попробует TLS-ALPN-01 на 443; если и он недоступен, сертификат не выпустится, в логах будет ошибка ACME. Откройте оба порта или переключитесь на DNS-проверку - для неё нужен плагин под вашего DNS-провайдера и пересборка бинарника через xcaddy.
Да, HTTP/3 поверх QUIC (UDP 443) включён по умолчанию начиная с Caddy 2.6. Клиенты, которые его не умеют, работают по HTTP/2. Проверьте, что firewall пропускает входящий UDP на 443, иначе часть клиентов молча откатится на HTTP/2.
Команда journalctl -u caddy | grep -i certificate покажет строки certificate obtained successfully. Файлы появятся в /var/lib/caddy/.local/share/caddy/certificates. Ещё быстрее - curl -vI https://ваш-домен и посмотреть строки про TLS-рукопожатие и издателя сертификата.
Для нескольких сайтов - десятки мегабайт памяти в покое, одного небольшого VPS (1 vCPU, 1 ГБ RAM) хватает с запасом. Точные числа зависят от трафика и количества одновременных соединений, поэтому проверяйте на своей нагрузке, а не по чужим цифрам.
Можно, но не на одних портах. Либо Caddy занимает 80 и 443, а Nginx слушает localhost и служит upstream, либо вы разводите их по разным портам. Одновременно занять 443 они не смогут - второй процесс не стартует.
caddy приносит systemd-сервис, конфиг-заготовку /etc/caddy/Caddyfile и системного пользователя caddy.root + file_server; обратный прокси - reverse_proxy localhost:3000.caddy validate, затем systemctl reload caddy (без простоя)./var/lib/caddy/.local/share/caddy, логи - journalctl -u caddy.